package com.dz.risk.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dz.risk.common.ResponseVO;
import com.dz.risk.common.enums.ReportFormsTypeEnum;
import com.dz.risk.common.utils.FutureUtils;
import com.dz.risk.common.utils.ThreadTaskCalculateUtil;
import com.dz.risk.common.utils.ZyCollectionUtils;
import com.dz.risk.custom.dynamic.ReportFormsCalculation;
import com.dz.risk.dao.ReportFormsDao;
import com.dz.risk.exception.VerifyException;
import com.dz.risk.pojo.domain.IndicatorDetailsEntity;
import com.dz.risk.pojo.domain.ReportCalculatedVersionManagement;
import com.dz.risk.pojo.domain.ReportFormsDataValueEntity;
import com.dz.risk.pojo.domain.ReportFormsEntity;
import com.dz.risk.pojo.model.CommonPageResult;
import com.dz.risk.pojo.model.PageModel;
import com.dz.risk.pojo.model.ReportFormsTreeModel;
import com.dz.risk.service.ReportCalculatedVersionManagementService;
import com.dz.risk.service.ReportFormsDataValueService;
import com.dz.risk.service.ReportFormsService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;


/**
 * @author Doke
 */
@Service("reportFormsService")
public class ReportFormsServiceImpl extends ServiceImpl<ReportFormsDao, ReportFormsEntity> implements ReportFormsService {

    private final ReportFormsDao reportFormsDao;

    private final ReportFormsDataValueService reportFormsDataValueService;

    private final ReportFormsCalculation reportFormsCalculation;

    private final ReportCalculatedVersionManagementService reportCalculatedVersionManagementService;

    @Autowired
    public ReportFormsServiceImpl(ReportFormsDao reportFormsDao, ReportFormsDataValueService reportFormsDataValueService, ReportFormsCalculation reportFormsCalculation, ReportCalculatedVersionManagementService reportCalculatedVersionManagementService) {
        this.reportFormsDao = reportFormsDao;
        this.reportFormsDataValueService = reportFormsDataValueService;
        this.reportFormsCalculation = reportFormsCalculation;
        this.reportCalculatedVersionManagementService = reportCalculatedVersionManagementService;
    }

    /**
     * 根据报表名称以及版本号查询报表基本信息
     *
     * @param reportFormsSerialNumber 报表编号
     * @param beginDate               开始时间
     * @param endDate                 结束时间
     * @return 报表基本信息
     */
    @Override
    public List<ReportFormsEntity> selectByReportFormsNameAndVersion(String reportFormsSerialNumber, Date beginDate, Date endDate) {
        QueryWrapper<ReportFormsEntity> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .eq(ReportFormsEntity::getReportFormsSerialNumber, reportFormsSerialNumber)
                .and(w -> w.and(a -> a.le(ReportFormsEntity::getBeginDate, beginDate).ge(ReportFormsEntity::getEndDate, beginDate)
                        ).or(b -> b.le(ReportFormsEntity::getBeginDate, beginDate).ge(ReportFormsEntity::getEndDate, beginDate))
                );

        return reportFormsDao.selectList(wrapper);
    }

    /**
     * 根据报表编号查询报表基本信息集合
     *
     * @param reportFormsSerialNumber 报表编号
     * @return 报表基本信息集合
     */
    @Override
    public List<ReportFormsEntity> selectByReportFormsSerialNumber(String reportFormsSerialNumber) {
        QueryWrapper<ReportFormsEntity> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .eq(!StringUtils.isEmpty(reportFormsSerialNumber), ReportFormsEntity::getReportFormsSerialNumber, reportFormsSerialNumber);
        return reportFormsDao.selectList(wrapper);
    }

    /**
     * 根据参数查询报表结构树
     *
     * @param pageModel               分页模型
     * @param reportFormsName         报表名称
     * @param reportFormsSerialNumber 报表编号
     * @return 报表结构树模型集合
     */
    @Override
    public CommonPageResult selectReportFormsTreeByParams(PageModel pageModel, String reportFormsName, String reportFormsSerialNumber) {
        PageHelper.startPage(pageModel.getPageNum(), pageModel.getPageSize());
        List<ReportFormsTreeModel> reportFormsTreeModels;

        //如果报表名称跟编号同时为空时则查询所有顶级报表信息
        if (StringUtils.isEmpty(reportFormsName) && StringUtils.isEmpty(reportFormsSerialNumber)) {
            reportFormsTreeModels = reportFormsDao.selectTopReport();
        } else {
            //根据参数查询报表树结构模型集合
            reportFormsTreeModels = reportFormsDao.selectReportFormsTreeByParams(reportFormsName, reportFormsSerialNumber);
        }

        //根据报表编号查询下属所有报表树信息
        reportFormsTreeModels.forEach(a -> a.setChildReportForms(selectReportFormsTreeBySuperiorReportFormsSerialNumber(a.getReportFormsSerialNumber())));
        PageInfo<ReportFormsTreeModel> reportFormsTreeModelPageInfo = new PageInfo<>(reportFormsTreeModels);

        return CommonPageResult
                .builder()
                .data(reportFormsTreeModelPageInfo.getList())
                .pages((long) reportFormsTreeModelPageInfo.getPages())
                .rows(pageModel.getPageSize())
                .total(reportFormsTreeModelPageInfo.getTotal())
                .page(pageModel.getPageNum())
                .build();
    }

    /**
     * 根据报表编号查询下属所有报表树信息
     *
     * @param reportFormsSerialNumber 报表编号Assert.notNull(reportFormsSerialNumber, "报表编号不能为空");
     * @return 报表信息树模型集合
     */
    private List<ReportFormsTreeModel> selectReportFormsTreeBySuperiorReportFormsSerialNumber(String reportFormsSerialNumber) {
        Assert.notNull(reportFormsSerialNumber, "报表编号不能为空");

        //根据报表编号查询下级报表
        List<ReportFormsTreeModel> reportFormsTreeModelList = reportFormsDao.selectReportFormsTreeBySuperiorReportFormsSerialNumber(reportFormsSerialNumber);

        //递归查询每级报表下级报表
        if (!CollectionUtils.isEmpty(reportFormsTreeModelList)) {
            reportFormsTreeModelList.forEach(a -> a.setChildReportForms(selectReportFormsTreeBySuperiorReportFormsSerialNumber(a.getReportFormsSerialNumber())));
        }
        return reportFormsTreeModelList;
    }

    /**
     * 查询所有报表基本信息(无版本得原始报表信息)
     *
     * @param reportFormsSerialNumber 报表编号
     * @param reportFormsName         报表名称
     * @return 报表基础信息集合
     */
    @Override
    public List<ReportFormsEntity> selectBasicInformationByParams(String reportFormsSerialNumber, String reportFormsName) {
        return reportFormsDao.selectBasicInformationByParams(reportFormsSerialNumber, reportFormsName);
    }

    /**
     * 根据报表编号删除报表基本信息以及下属报表
     *
     * @param reportFormsSerialNumber 报表编号
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteByReportFormsSerialNumber(String reportFormsSerialNumber) {
        //先查寻出该报表下级所有报表结构
        List<ReportFormsTreeModel> reportFormsTreeModelList = selectReportFormsTreeBySuperiorReportFormsSerialNumber(reportFormsSerialNumber);

        //根据报表树结构模型信息删除包含的所有报表(如果某一个报表已经产生报表版本信息,则不允许在删除)
        deleteByReportFormsList(reportFormsTreeModelList);

        //根据报表编号删除报表计算版本信息
        reportCalculatedVersionManagementService.deleteByReportFormsSerialNumber(reportFormsSerialNumber);

        //根据报表编号删除报表信息
        deleteAllByReportFormsSerialNumber(reportFormsSerialNumber);
    }

    /**
     * 查询所有报表基本信息(已导入版本得报表数据)
     *
     * @param pageModel               分页模型
     * @param reportFormsSerialNumber 报表编号
     * @param reportFormsName         报表名称
     * @param reportFormsBeginDate    报表开始时间
     * @param reportFormsEndDate      报表结束时间
     * @return 报表基础信息集合
     */
    @Override
    public CommonPageResult selectVersionReportFormsByParams(PageModel pageModel, String reportFormsSerialNumber, String reportFormsName, Date reportFormsBeginDate, Date reportFormsEndDate) {
        PageHelper.startPage(pageModel.getPageNum(), pageModel.getPageSize());
        PageInfo<ReportFormsEntity> reportFormsEntityPageInfo = new PageInfo<>(reportFormsDao.selectVersionReportFormsByParams(reportFormsSerialNumber, reportFormsName, reportFormsBeginDate, reportFormsEndDate));

        return CommonPageResult
                .builder()
                .data(reportFormsEntityPageInfo.getList())
                .pages((long) reportFormsEntityPageInfo.getPages())
                .rows(pageModel.getPageSize())
                .total(reportFormsEntityPageInfo.getTotal())
                .page(pageModel.getPageNum())
                .build();
    }

    @Override
    public ResponseVO update(ReportFormsEntity reportFormsEntity) {
        QueryWrapper<ReportFormsEntity> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .eq(!StringUtils.isEmpty(reportFormsEntity.getReportFormsName()), ReportFormsEntity::getReportFormsName, reportFormsEntity.getReportFormsName());
        List<ReportFormsEntity> reportFormsEntities = reportFormsDao.selectList(wrapper);
        if (!CollectionUtils.isEmpty(reportFormsEntities)) {
            return ResponseVO.fail("报表名称已存在");
        }
        reportFormsDao.updateExt(reportFormsEntity);
        return ResponseVO.ok();
    }

    /**
     * 根据单元格编号以及版本区间获取该区间版本的该单元格所属的报表基本信息
     *
     * @param cellSerialNumber 单元格编号
     * @param versionBeginDate 版本开始时间
     * @param versionEndDate   版本结束时间
     * @return 报表基本信息
     */
    @Override
    public List<ReportFormsEntity> selectByCellSerialNumberAndBeginDateAndEndDate(String cellSerialNumber, Date versionBeginDate, Date versionEndDate) {
        return reportFormsDao.selectByCellSerialNumberAndBeginDateAndEndDate(cellSerialNumber, versionBeginDate, versionEndDate);
    }

    /**
     * 查询报表版本开始结束时间等于参数开始结束时间的报表信息集合
     *
     * @param beginDate       开始时间
     * @param endDate         结束时间
     * @param reportType      报表类型
     * @param reportFormsName 报表名称
     * @return 报表基本信息集合
     */
    @Override
    public List<ReportFormsEntity> selectReportFormsByBeginDateAndEndDate(Date beginDate, Date endDate, ReportFormsTypeEnum reportType, String reportFormsName) {
        QueryWrapper<ReportFormsEntity> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .eq(ReportFormsEntity::getBeginDate, beginDate)
                .eq(ReportFormsEntity::getEndDate, endDate)
                .eq(Objects.nonNull(reportType), ReportFormsEntity::getReportType, reportType)
                .like(!StringUtils.isEmpty(reportFormsName), ReportFormsEntity::getReportFormsName, reportFormsName);
        return reportFormsDao.selectList(wrapper);
    }

    /**
     * 根据旧报表模板ID生成新版本报表模板
     *
     * @param reportFormsEntity 报表基本信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void generateNewVersionReportFormsByReportFormsId(ReportFormsEntity reportFormsEntity) {
        //生成新版报表基本信息数据ID
        String reportFormsId = IdWorker.getIdStr();

        //根据旧版本报表Id查询出下属所有模板内容数据集合
        List<ReportFormsDataValueEntity> reportFormsDataValueEntities = reportFormsDataValueService.selectByParams(ReportFormsDataValueEntity
                .builder()
                .reportFormsId(reportFormsEntity.getId())
                .build());

        //保存新版报表基本信息
        reportFormsDao.insert(reportFormsEntity.setId(reportFormsId));

        //处理报表数据值集合并重新插入数据库
        processAndReinsertTheDatabase(reportFormsId, reportFormsDataValueEntities);
    }

    /**
     * 报表模板数据计算
     *
     * @param calculatingTime 计算时间
     * @param reportFormsIds  模板ID集合
     */
    @Override
    public void reportFormsTemplateDataCalculate(Date calculatingTime, List<String> reportFormsIds) throws InterruptedException, BrokenBarrierException, TimeoutException, ExecutionException {
        //遍历计算需要计算的模板
        for (String reportFormsId : reportFormsIds) {
            //根据Id查询报表基本信息
            ReportFormsEntity reportFormsEntity = reportFormsDao.selectById(reportFormsId);
            Assert.notNull(reportFormsEntity, "报表不存在");

            //根据报表ID获取该报表模板数据
            List<ReportFormsDataValueEntity> formsDataValueEntities = reportFormsDataValueService.selectReportTemplateByReportFormsId(reportFormsId);
            Assert.notEmpty(formsDataValueEntities, "报表模板内暂无任何数据值");

            //先删除报表下相同计算版本的报表相关计算信息及数据
            reportCalculatedVersionManagementService.deleteByReportFormsIdAndCalculatingTime(reportFormsEntity.getId(), calculatingTime);

            //计算每个线程实际任务处理量
            int taskLength = ThreadTaskCalculateUtil.geTeachThreadTaskLength(ThreadTaskCalculateUtil.IO_CONCENTRATED, formsDataValueEntities.size(), 30);

            //根据线程实际任务数据处理量拆分集合
            List<List<ReportFormsDataValueEntity>> split = ZyCollectionUtils.split(formsDataValueEntities, taskLength);

            List<Future<Integer>> taskResults = new ArrayList<>();
            CyclicBarrier barrier = new CyclicBarrier(split.size());
            AtomicLong processingDataLength = new AtomicLong();
            for (List<ReportFormsDataValueEntity> reportFormsDataValueEntities : split) {
                taskResults.add(reportFormsCalculation.reportFormsDataValueCalculation(formsDataValueEntities.size(), reportFormsDataValueEntities, reportFormsEntity, calculatingTime, barrier, processingDataLength));
            }

            //处理量返回值集合
            List<Integer> integers = FutureUtils.getValues(taskResults);
            if (formsDataValueEntities.size() != integers.parallelStream().mapToInt(Integer::intValue).sum()) {
                throw new VerifyException(reportFormsEntity.getReportFormsName() + "计算失败");
            }
            //生成新计算报表版本信息
            processingReportCalculationVersion(reportFormsEntity, calculatingTime);
        }

    }

    /**
     * 生成新计算报表版本信息
     *
     * @param reportFormsEntity 报表基本信息
     * @param calculatingTime   计算版本
     */
    private void processingReportCalculationVersion(ReportFormsEntity reportFormsEntity, Date calculatingTime) {
        //生成新的报表计算版本信息
        reportCalculatedVersionManagementService.save(ReportCalculatedVersionManagement
                .builder()
                .reportFormsId(reportFormsEntity.getId())
                .reportFormsName(reportFormsEntity.getReportFormsName())
                .reportFormsSerialNumber(reportFormsEntity.getReportFormsSerialNumber())
                .beginDate(reportFormsEntity.getBeginDate())
                .endDate(reportFormsEntity.getEndDate())
                .calculatedVersion(calculatingTime)
                .build());
    }

    /**
     * 通过名称或者编号查询出list
     *
     * @param reportFormsEntity
     * @return
     */
    @Override
    public List<ReportFormsEntity> selectByNameOrNumber(ReportFormsEntity reportFormsEntity) {
        return reportFormsDao.selectByNameOrNumber(reportFormsEntity);
    }

    /**
     * 处理报表数据值集合并重新插入数据库
     *
     * @param reportFormsId                报表ID
     * @param reportFormsDataValueEntities 报表数据值集合
     */
    private void processAndReinsertTheDatabase(String reportFormsId, List<ReportFormsDataValueEntity> reportFormsDataValueEntities) {
        //讲数据ID置空并设置所属报表ID
        reportFormsDataValueEntities.forEach(a -> a.setId(null).setReportFormsId(reportFormsId));

        reportFormsDataValueService.saveBatch(reportFormsDataValueEntities);
    }

    /**
     * 根据报表树结构模型信息删除包含的所有报表(如果某一个报表已经产生报表版本信息,则不允许在删除)
     *
     * @param reportFormsTreeModelList 报表树结构模型
     */
    private void deleteByReportFormsList(List<ReportFormsTreeModel> reportFormsTreeModelList) {
        if (!CollectionUtils.isEmpty(reportFormsTreeModelList)) {
            reportFormsTreeModelList.forEach(a -> {

                //如果下级报表集合不为空则递归删除
                if (!CollectionUtils.isEmpty(a.getChildReportForms())) {
                    deleteByReportFormsList(a.getChildReportForms());
                }

                //根据报表编号查询有效的该表编号报表版本数据信息
                List<ReportFormsEntity> reportFormsEntities = reportFormsDao.selectValidVersionDataByReportFormsSerialNumber(a.getReportFormsSerialNumber());
                //如果该报表已存在报表版本数据则禁止删除
                if (!CollectionUtils.isEmpty(reportFormsEntities)) {
                    throw new VerifyException("该报表或下级报表已存在报表版本数据,禁止删除");
                }

                //根据报表编号删除报表基本信息
                deleteAllByReportFormsSerialNumber(a.getReportFormsSerialNumber());
            });
        }
    }

    /**
     * 根据报表编号删除报表信息
     *
     * @param reportFormsSerialNumber 报表编号
     */
    private void deleteAllByReportFormsSerialNumber(String reportFormsSerialNumber) {
        Assert.notNull(reportFormsSerialNumber, "报表编号不能为空");

        //查询是否该编号报表已经生成版本数据
        List<ReportFormsEntity> reportFormsEntities = reportFormsDao.selectValidVersionDataByReportFormsSerialNumber(reportFormsSerialNumber);
        if (!CollectionUtils.isEmpty(reportFormsEntities)) {
            throw new VerifyException("该报表或下级报表已存在报表版本数据,禁止删除");
        }

        UpdateWrapper<ReportFormsEntity> wrapper = new UpdateWrapper<>();
        wrapper.lambda()
                .eq(ReportFormsEntity::getReportFormsSerialNumber, reportFormsSerialNumber);
        reportFormsDao.delete(wrapper);
    }

    /**
     * 判断新增和修改的时候 报表名和报表编号是否存在
     *
     * @param reportFormsEntity
     * @return
     */
    @Override
    public ResponseVO queryOnly(ReportFormsEntity reportFormsEntity) {
        List<ReportFormsEntity> reportFormsEntities = selectByNameOrNumber(reportFormsEntity);
        for (ReportFormsEntity formsEntity : reportFormsEntities) {
            if (formsEntity.getReportFormsName().equals(reportFormsEntity.getReportFormsName())) {
                return ResponseVO.fail("报表名已存在：" + formsEntity.getReportFormsName());
            }
            if (reportFormsEntity.getId() == null) {
                if (formsEntity.getReportFormsSerialNumber().equals(reportFormsEntity.getReportFormsSerialNumber())) {
                    return ResponseVO.fail("报表编号已存在：" + formsEntity.getReportFormsSerialNumber());
                }
            }
        }
        return null;
    }

    /**
     * 根据版本批量计算报表
     *
     * @param beginDate       开始时间
     * @param endDate         结束时间
     * @param calculatingTime 计算版本
     */
    @Override
    public void reportFormsCalculateBatchByVersion(Date beginDate, Date endDate, Date calculatingTime) throws InterruptedException, BrokenBarrierException, TimeoutException, ExecutionException {
        //根据开始结束时间查询此区间版本报表
        QueryWrapper<ReportFormsEntity> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .eq(ReportFormsEntity::getBeginDate, beginDate)
                .eq(ReportFormsEntity::getEndDate, endDate);
        List<ReportFormsEntity> reportFormsEntities = reportFormsDao.selectList(wrapper);

        //如果此区间有报表模板,则遍历进行计算
        if (!CollectionUtils.isEmpty(reportFormsEntities)) {
            List<String> reportFormIds = reportFormsEntities.stream().map(ReportFormsEntity::getId).collect(Collectors.toList());
            //先删除改计算时间已经计算出的所有报表结果,准备重新计算
            reportFormsDataValueService.deleteByVersion(calculatingTime);

            this.reportFormsTemplateDataCalculate(calculatingTime, reportFormIds);
        }
    }

}